package lol.clann.minecraft.springboot.plugin.bukkit.impl.raytrace;

import lol.clann.minecraft.springboot.api.annotation.compatibility.CompatibilityChecked;
import lol.clann.minecraft.springboot.api.bukkit.constant.FluidCollisionOptionEnum;
import lol.clann.minecraft.springboot.api.bukkit.model.RayTraceResult;
import lol.clann.minecraft.springboot.api.bukkit.utils.EntityUtils;
import lol.clann.minecraft.springboot.api.bukkit.utils.RayTraceUtils;
import lol.clann.minecraft.springboot.api.bukkit.utils.VectorUtils;
import lol.clann.minecraft.springboot.api.bukkit.utils.WorldUtils;
import lol.clann.minecraft.springboot.bukkit.utils.BukkitUtils;
import lombok.extern.slf4j.Slf4j;
import net.minecraft.server.v1_13_R2.FluidCollisionOption;
import net.minecraft.server.v1_14_R1.MovingObjectPositionBlock;
import net.minecraft.server.v1_14_R1.MovingObjectPositionEntity;
import net.minecraft.server.v1_14_R1.RayTrace;
import net.minecraft.server.v1_7_R4.MovingObjectPosition;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

/**
 *
 * @author pyz
 * @date 2019/7/12 11:55 PM
 */
@Slf4j
@Service
@CompatibilityChecked
public class RayTraceUtilsImpl_all implements RayTraceUtils {

    @Autowired
    private VectorUtils vectorUtils;
    @Autowired
    private WorldUtils worldUtils;
    @Autowired
    private EntityUtils entityUtils;

    private int version = BukkitUtils.getBukkitVersionNumber() / 100;

    @PostConstruct
    private void init() {

    }

    @Override
    public RayTraceResult rayTraceBlocks(Location start, Vector direction, double maxDistance, FluidCollisionOptionEnum fluidCollisionOption, boolean ignorePassableBlocks) {
        if (BukkitUtils.getBukkitVersionNumber() > 11300) {
            org.bukkit.util.RayTraceResult ray = ((org.bukkit.craftbukkit.v1_13_R2.CraftWorld) start.getWorld()).rayTraceBlocks(start, direction, maxDistance, FluidCollisionMode.valueOf(fluidCollisionOption.name()), ignorePassableBlocks);
            return new RayTraceResult(ray.getHitPosition(), ray.getHitBlock(), ray.getHitBlockFace(), ray.getHitEntity());
        }
        Object movingObjectPosition = rayTrace(start, direction, maxDistance, fluidCollisionOption, ignorePassableBlocks);
        return from(start.getWorld(), movingObjectPosition);
    }

    protected <T> T rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionOptionEnum fluidCollisionOption, boolean ignorePassableBlocks) {
        Vector from = start.toVector();
        Vector to = from.clone().add(direction.clone().normalize().multiply(maxDistance));
        Object movingObjectPosition;
        if (version < 113) {
            movingObjectPosition = ((org.bukkit.craftbukkit.v1_7_R4.CraftWorld) start.getWorld()).getHandle().rayTrace(
                    vectorUtils.toVec3D(from),
                    vectorUtils.toVec3D(to),
                    fluidCollisionOption != FluidCollisionOptionEnum.NEVER,
                    ignorePassableBlocks,
                    false
            );
        } else if (version < 114) {
            movingObjectPosition = ((org.bukkit.craftbukkit.v1_13_R2.CraftWorld) start.getWorld()).getHandle().rayTrace(
                    vectorUtils.toVec3D(from),
                    vectorUtils.toVec3D(to),
                    FluidCollisionOption.valueOf(fluidCollisionOption.name()),
                    ignorePassableBlocks,
                    false
            );
        } else {
            movingObjectPosition = ((org.bukkit.craftbukkit.v1_14_R1.CraftWorld) start.getWorld()).getHandle().rayTrace(
                    new RayTrace(vectorUtils.toVec3D(from),
                            vectorUtils.toVec3D(to),
                            ignorePassableBlocks ? RayTrace.BlockCollisionOption.COLLIDER : RayTrace.BlockCollisionOption.OUTLINE,
                            RayTrace.FluidCollisionOption.valueOf(fluidCollisionOption.name()),
                            null
                    )
            );
        }
        return (T) movingObjectPosition;
    }

    protected RayTraceResult from(World world, Object rayTraceResult) {
        Vector hitPosition = null;
        Block hitBlock = null;
        BlockFace hitBlockFace = null;
        Entity hitEntity = null;
        if (BukkitUtils.getBukkitVersionNumber() < 10800) {
//            1.7
            MovingObjectPosition movingObjectPosition = (MovingObjectPosition) rayTraceResult;
            hitPosition = vectorUtils.fromVec3D(movingObjectPosition.pos);
            hitBlock = world.getBlockAt(movingObjectPosition.b, movingObjectPosition.c, movingObjectPosition.d);
            if (movingObjectPosition.type != null) {
                hitBlockFace = BlockFace.valueOf(movingObjectPosition.type.name());
            }
            if (movingObjectPosition.entity != null) {
                hitEntity = entityUtils.toBukkit(movingObjectPosition.entity);
            }
        } else if (BukkitUtils.getBukkitVersionNumber() < 11300) {
//            1.8
            net.minecraft.server.v1_8_R3.MovingObjectPosition movingObjectPosition = (net.minecraft.server.v1_8_R3.MovingObjectPosition) rayTraceResult;
            hitPosition = vectorUtils.fromVec3D(movingObjectPosition.pos);
            if (movingObjectPosition.a() != null) {
                hitBlock = world.getBlockAt(movingObjectPosition.a().getX(), movingObjectPosition.a().getY(), movingObjectPosition.a().getZ());
            }
            if (movingObjectPosition.type != null) {
                hitBlockFace = BlockFace.valueOf(movingObjectPosition.type.name());
            }
            hitEntity = entityUtils.toBukkit(movingObjectPosition.entity);
        } else if (BukkitUtils.getBukkitVersionNumber() < 11400) {
//            1.13
            net.minecraft.server.v1_13_R2.MovingObjectPosition movingObjectPosition = (net.minecraft.server.v1_13_R2.MovingObjectPosition) rayTraceResult;
            hitPosition = vectorUtils.fromVec3D(movingObjectPosition.pos);
            if (movingObjectPosition.getBlockPosition() != null) {
                hitBlock = world.getBlockAt(movingObjectPosition.getBlockPosition().getX(), movingObjectPosition.getBlockPosition().getY(), movingObjectPosition.getBlockPosition().getZ());
            }
            if (movingObjectPosition.type != null) {
                hitBlockFace = BlockFace.valueOf(movingObjectPosition.type.name());
            }
            hitEntity = entityUtils.toBukkit(movingObjectPosition.entity);
        } else {
//            1.14
            net.minecraft.server.v1_14_R1.MovingObjectPosition movingObjectPosition = (net.minecraft.server.v1_14_R1.MovingObjectPosition) rayTraceResult;
            hitPosition = vectorUtils.fromVec3D(movingObjectPosition.getPos());
            if (movingObjectPosition instanceof MovingObjectPositionBlock) {
                MovingObjectPositionBlock b = (MovingObjectPositionBlock) movingObjectPosition;
                hitBlock = world.getBlockAt(b.getBlockPosition().getX(), b.getBlockPosition().getY(), b.getBlockPosition().getZ());
                hitBlockFace = BlockFace.valueOf(b.getType().name());
            } else if (movingObjectPosition instanceof MovingObjectPositionEntity) {
                MovingObjectPositionEntity e = (MovingObjectPositionEntity) movingObjectPosition;
                hitEntity = entityUtils.toBukkit(e.getEntity());
            }
        }
        return new RayTraceResult(hitPosition, hitBlock, hitBlockFace, hitEntity);
    }
}
